package it.gtd.swimpool.model.business;

import com.google.common.annotations.VisibleForTesting;
import it.gtd.swimpool.model.entity.User;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.LocalBean;
import javax.ejb.Singleton;
import java.util.*;

import static com.google.common.base.Preconditions.*;

@Singleton
@LocalBean
public class ActivationManager {
    private Thread worker;
    private static final int MINUTES = 60;
    private Map<String, User> activationMap;

    @VisibleForTesting
    boolean stopped;

    public ActivationManager() {
        stopped = false;
        this.worker = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!stopped) try {
                    Thread.sleep(MINUTES * 60 * 1000);
                    ActivationManager.this.removeOlder();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        activationMap = new HashMap<String, User>();
    }

    public String createActivationHash(User user) {
        checkNotNull(user, "user can't be null");
        checkArgument(!activationMap.containsValue(user), "user is already present in the mapping");

        String key = generateUserActivationHash(user);
        activationMap.put(key, user);

        return key;
    }

    public User get(String hash) {
        checkNotNull(hash, "hash can't be null");

        User user = activationMap.get(hash);

        checkState(user != null, "No hash for user");
        return user;
    }

    public void removeActivationHash(String hash) {
        checkNotNull(hash, "hash can't be null");
        activationMap.remove(hash);
    }

    private void removeOlder() {
        Calendar calendar = Calendar.getInstance();
        Date now = new Date();

        calendar.setTime(now);
        calendar.add(Calendar.HOUR, -1);

        Date oneHourAgo = calendar.getTime();

        List<String> oldHashes = new ArrayList<String>();
        for (Map.Entry<String, User> entry : activationMap.entrySet()) {
            Date creationDate = entry.getValue().getCreationDate();
            if (creationDate.before(oneHourAgo)) {
                oldHashes.add(entry.getKey());
            }
        }

        for (String oldHash : oldHashes) {
            activationMap.remove(oldHash);
        }
    }

    private String generateUserActivationHash(User user) {
        checkNotNull(user, "user can't be null");

        Date now = new Date();

        //byte[] hash = Hashing.md5().hashString(user.getEmail() + now.toString()).asBytes();

        //String newHash = new String(hash);

        String newHash = Integer.toString(now.hashCode() + new Random(1000000000).nextInt());

        if (activationMap.get(newHash) != null) {
            return generateUserActivationHash(user);
        } else {
            return newHash;
        }

    }

    @PostConstruct
    @VisibleForTesting
    void start() {
        this.worker.start();
    }

    @PreDestroy
    @VisibleForTesting
    void dispose() {
        this.stopped = true;
        try {
            this.worker.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
